package de.lighti.starcraft.model;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.nio.ByteBuffer;

public class Match implements Serializable {

    /**
     * 
     */
    private static final long serialVersionUID = 8922106968762613003L;
    private StarcraftBot homeBot;
    private StarcraftBot awayBot;
    private StarcraftMap map;

    private transient ByteBuffer replay;

    /** True if the host won the game */
    private boolean hostwon;
    /** This games id number */
    private final int gameID;
    /** The time at which this game started */
    private long time;
    /** True if this game was a draw forcing a determination based on score */
    private boolean wasDraw;
    /** The score the host bot aquired */
    private int hostScore;
    /** The score the away bot aquired */
    private int awayScore;
    /** True if the home bot crashed */
    private boolean hostcrash;
    /** True if the away bot crashed */
    private boolean awaycrash;
    /** Number of times the host bot exceeds 55ms on a frame */
    private int hostTimer55ms;
    /** Number of times the host bot exceeds 1sec on a frame */
    private int hostTimer1sec;
    /** Number of times the host bot exceeds 10sec on a frame */
    private int hostTimer10sec;
    /** Number of times the away bot exceeds 55ms on a frame */
    private int awayTimer55ms;
    /** Number of times the away bot exceeds 1sec on a frame */
    private int awayTimer1sec;
    /** Number of times the away bot exceeds 10sec on a frame */
    private int awayTimer10sec;
    /** If this game timedout */
    private boolean timeout;
    /** The final frame in this game. Only valid if the game didn't timeout */
    private int finalFrame;

    private long hostTime = 0;

    private long guestTime = 0;

    private int updatesReceived;
    private final int round;

    public final static String FIELDS_HEADER = "#gameID, round, homeBot, awayBot, map, hostwon, hostcrash, awaycrash, hostTimer55ms, hostTimer1sec, "
                    + "hostTimer10sec, awayTimer55ms, awayTimer1sec, awayTimer10sec, wasDraw, hostScore, awayScore, finalFrame, (int) hostTime,"
                    + "guestTime\n";

    public Match( int gameid, int round, StarcraftBot one, StarcraftBot two, StarcraftMap map ) {
        super();
        if (one == null || two == null || map == null) {
            throw new IllegalArgumentException( "arguments may not be null" );
        }

        homeBot = one;
        awayBot = two;
        this.map = map;
        this.round = round;
        gameID = gameid;
    }

    /**
     * Calculates if the host won based on scores. Only does this if the game
     * was a draw and the tournament module ended it.
     */
    private void calcResult() {
        if (wasDraw && hostScore != 0 && awayScore != 0) {

            if (hostScore > awayScore) {
                hostwon = true;

            }
            else {
                hostwon = false;
            }
        }
    }

    public boolean canBeDumped() {
        int updatesExpected = 0;
        updatesExpected += homeBot.isBenchmarkBot() ? 0 : 1;
        updatesExpected += awayBot.isBenchmarkBot() ? 0 : 1;

        return updatesExpected == updatesReceived;
    }

    public StarcraftBot getAwayBot() {
        return awayBot;
    }

    public int getAwayScore() {
        return awayScore;
    }

    public int getAwayTimer10sec() {
        return awayTimer10sec;
    }

    public int getAwayTimer1sec() {
        return awayTimer1sec;
    }

    public int getAwayTimer55ms() {
        return awayTimer55ms;
    }

    public int getFinalFrame() {
        return finalFrame;
    }

    public int getGameID() {
        return gameID;
    }

    public long getGuestTime() {
        return guestTime;
    }

    public StarcraftBot getHomeBot() {
        return homeBot;
    }

    public int getHostScore() {
        return hostScore;
    }

    public long getHostTime() {
        return hostTime;
    }

    public int getHostTimer10sec() {
        return hostTimer10sec;
    }

    public int getHostTimer1sec() {
        return hostTimer1sec;
    }

    public int getHostTimer55ms() {
        return hostTimer55ms;
    }

    public StarcraftMap getMap() {
        return map;
    }

    public ByteBuffer getReplay() {
        return replay;
    }

    public int getRound() {
        return round;
    }

    public long getTime() {
        return time;
    }

    public boolean isAwaycrash() {
        return awaycrash;
    }

    public boolean isHostcrash() {
        return hostcrash;
    }

    public boolean isHostwon() {
        return hostwon;
    }

    public boolean isTimeout() {
        return timeout;
    }

    public boolean isWasDraw() {
        return wasDraw;
    }

    private void readObject( ObjectInputStream ois ) throws ClassNotFoundException, IOException {
        // default deserialization
        ois.defaultReadObject();

        //Transient field
        final boolean hasbinary = ois.readBoolean();

        if (hasbinary) {
            final byte[] bytes = (byte[]) ois.readObject();
            replay = ByteBuffer.wrap( bytes );
        }

    }

    public String replayPath( boolean isHost ) {
        final StarcraftBot thisBot = isHost ? getHomeBot() : getAwayBot();
        return "maps\\replays\\bwapi\\" + getHomeBot().getName() + "\\" + String.format( "%03d", getGameID() ) + "-" + thisBot.getName() + "_vs_"
                        + getAwayBot().getName() + ".rep";
    }

    public void reset() {
        hostwon = false;
        time = 0;
        wasDraw = false;
        hostScore = 0;
        awayScore = 0;
        hostcrash = false;
        awaycrash = false;
        hostTimer55ms = 0;
        hostTimer1sec = 0;
        hostTimer10sec = 0;
        awayTimer55ms = 0;
        awayTimer1sec = 0;
        awayTimer10sec = 0;
        timeout = false;
        finalFrame = 0;
        hostTime = 0;
        guestTime = 0;
        updatesReceived = 0;
        replay = null;

    }

    public void setAwaycrash( boolean b ) {
        awaycrash = b;

    }

    public void setAwayScore( int selfScore ) {
        awayScore = selfScore;

    }

    public void setAwayTimer10sec( int timeOut10s ) {
        awayTimer10sec = timeOut10s;

    }

    public void setAwayTimer1sec( int timeOut1s ) {
        awayTimer1sec = timeOut1s;

    }

    public void setAwayTimer55ms( int timeOut55ms ) {
        awayTimer55ms = timeOut55ms;

    }

    public void setFinalFrame( int frameCount ) {
        finalFrame = frameCount;

    }

    public void setGuestTime( long elpasedTime ) {

        guestTime = elpasedTime;
    }

    public void setHostcrash( boolean b ) {
        hostcrash = b;

    }

    public void setHostScore( int selfScore ) {
        hostScore = selfScore;

    }

    public void setHostTime( long elpasedTime ) {
        hostTime = elpasedTime;

    }

    public void setHostTimer10sec( int timeOut10s ) {
        hostTimer10sec = timeOut10s;

    }

    public void setHostTimer1sec( int timeOut1s ) {
        hostTimer1sec = timeOut1s;

    }

    public void setHostTimer55ms( int timeOut55ms ) {
        hostTimer55ms = timeOut55ms;

    }

    public void setHostwon( boolean b ) {
        hostwon = b;

    }

    public void setMap( StarcraftMap map ) {
        this.map = map;
    }

    public void setOne( StarcraftBot one ) {
        homeBot = one;
    }

    public void setReplay( ByteBuffer replayBuffer ) {
        replay = replayBuffer;

    }

    public void setTime( long time ) {
        this.time = time;
    }

    public void setTimeout( boolean b ) {
        timeout = b;

    }

    public void setTwo( StarcraftBot two ) {
        awayBot = two;
    }

    public void setWasDraw( boolean b ) {
        wasDraw = b;

    }

    @Override
    public String toString() {
        final String s = String.format( "%7d, %5d, %20s, %20s, %35s, %6b, %6b, %6b, %5d, %5d, %5d, %5d, %5d, %5d, %6b, %8d, %8d, %8d, %10d, %10d \n", gameID,
                        round, homeBot.getName(), awayBot.getName(), map.getName().replace( ' ', '_' ), hostwon, hostcrash, awaycrash, hostTimer55ms,
                        hostTimer1sec, hostTimer10sec, awayTimer55ms, awayTimer1sec, awayTimer10sec, wasDraw, hostScore, awayScore, finalFrame, (int) hostTime,
                        (int) guestTime );
        return s;
    }

    public void update( Match game ) {

        if (game.getHostTimer55ms() > 0) {
            setHostTimer55ms( game.getHostTimer55ms() );
        }
        if (game.getHostTimer1sec() > 0) {
            setHostTimer1sec( game.getHostTimer1sec() );
        }
        if (game.getHostTimer10sec() > 0) {
            setHostTimer10sec( game.getHostTimer10sec() );
        }
        if (game.getAwayTimer55ms() > 0) {
            setAwayTimer55ms( game.getAwayTimer55ms() );
        }
        if (game.getAwayTimer1sec() > 0) {
            setAwayTimer1sec( game.getAwayTimer1sec() );
        }
        if (game.getAwayTimer10sec() > 0) {
            setAwayTimer10sec( game.getAwayTimer10sec() );
        }
        setFinalFrame( game.getFinalFrame() );
        setWasDraw( game.isWasDraw() );
        setHostwon( game.isHostwon() );
        setTimeout( game.isTimeout() );
        if (game.getHostTime() > 0) {
            setHostTime( game.getHostTime() );
        }
        if (game.getGuestTime() > 0) {
            setGuestTime( game.getGuestTime() );
        }
        if (game.getHostScore() > 0) {
            setHostScore( game.getHostScore() );
        }
        if (game.getAwayScore() > 0) {
            setAwayScore( game.getAwayScore() );
        }
        if (game.isHostcrash()) {
            if (isAwaycrash()) {
                setHostwon( true );
            }
            else {
                setHostwon( false );
                setHostcrash( true );
            }
        }
        else if (game.isAwaycrash()) {
            if (isHostcrash()) {
                setHostwon( false );
            }
            else {
                setHostwon( true );
                setAwaycrash( true );
            }
        }
        if (game.isTimeout()) {
            calcResult();
        }
        if (replay == null) {
            setReplay( game.getReplay() );
        }

        updatesReceived++;
    }

    private void writeObject( ObjectOutputStream oos ) throws IOException {
        // default serialization 
        oos.defaultWriteObject();

        //Transient field

        if (replay != null) {
            oos.writeBoolean( true );
            replay.rewind();
            oos.writeObject( replay.array() );

        }
        else {
            oos.writeBoolean( false );
        }
    }
}